module EStep {
    'use strict';

    export class Socket {
        public static $inject = ['$interval', '$rootScope', 'PluginService'];
        /*
         * 发起心跳的条件：
         *   1. 5s发起一个心跳消息
         *   2. 任何返回都会重置心跳消息计数
         *   3. 累计3次心跳消息没有返回，则断开连接
         */
        private heartbeat_msg: string     = 'heartbeat';
        private heartbeat_interval        = null;
        private missed_heartbeats: number = 0;
        private num__interval: number     = 0;

        private ws: WebSocket     = null;
        private url: string;
        private onMessageCallBack = [];
        private _statusChangeCallBack: (socketStatus: number) => void;

        constructor(private $interval, private $rootScope, private PluginService: PluginService) {
        }

        //region private method
        private safeDigest(autoApply): void {
            if (autoApply && !this.$rootScope.$$phase) {
                this.$rootScope.$digest();
            }
        }

        public setSocketUrl(url: string): void {
            this.url = 'ws://' + url;
        }

        /**
         * 设置WebSocket状态，然后调用回调
         */
        private setStatus(closeCalled: boolean = false) {
            const status = this.ws ? this.ws.readyState : WebSocket.CLOSED;
            this._statusChangeCallBack.apply(this, [status, closeCalled]);
        }

        private clearInterval(): void {
            if (this.heartbeat_interval == null) return;
            this.$interval.cancel(this.heartbeat_interval);
            this.heartbeat_interval = null;
            this.missed_heartbeats  = 0;
            this.num__interval      = 0;
        }

        private onOpen = (): void => {
            this.safeDigest(true);
            this.setStatus();
            if (this.heartbeat_interval != null)  return;

            const base = this;

            base.heartbeat_interval = base.$interval(function () {
                base.num__interval++;
                if (base.heartbeat_interval != null && base.num__interval >= 5) {
                    base.num__interval = 0;
                    try {
                        base.missed_heartbeats++;
                        if (base.missed_heartbeats >= 3) {
                            //noinspection ExceptionCaughtLocallyJS
                            throw new Error("Too many missed heartbeats.");
                        }
                        base.ws.send(base.heartbeat_msg);
                        console.log('Background run：' + base.PluginService.IsReady() + '. Send heartbeats ' + (new Date()).toTimeString());
                    } catch (e) {
                        base.close();
                        base.clearInterval();
                    }
                }
            }, 1000);
        };

        private onMessage = (event): void => {
            this.missed_heartbeats = 0;
            this.num__interval     = 0;

            if (event.data === this.heartbeat_msg) {
                return;
            }
            const self = this;
            const args = [];
            args.push(event.data);
            angular.forEach(this.onMessageCallBack, callback => {
                callback.fn.apply(self, args);
            });

            this.safeDigest(true);
        };

        private onClose = (): void => {
            this.safeDigest(true);
            this.clearInterval();
            this.setStatus(true);
        };

        private onError = (event): void => {
            this.setStatus();
        };

        //endregion private method

        /**
         * 添加状态检测回调函数
         * @param callback
         */
        public setStatusChangeCallback(callback) {
            this._statusChangeCallBack = callback;
        }

        /**
         * 添加消息回调函数
         * @param callback
         */
        public addMessageHandler(callback) {
            this.onMessageCallBack.push({
                fn: callback
            })
        }

        /**
         * 建立连接
         */
        public connect() {
            console.log('connect');
            if (this.ws != null) {
                if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
                    this.setStatus();
                    return;
                }
                this.ws.close();
                this.ws = null;
            }
            this.ws           = new WebSocket(this.url);
            this.ws.onopen    = this.onOpen;
            this.ws.onmessage = this.onMessage;
            this.ws.onclose   = this.onClose;
            this.ws.onerror   = this.onError;
            this.setStatus();
        }

        /**
         * 获取Socket的状态
         * @returns {any}
         */
        public getStatus(): number {
            return this.ws ? this.ws.readyState : WebSocket.CLOSED;
        }

        /**
         * 发送消息
         * @param message
         */
        public send(message): void {
            if (this.ws == null || this.ws.readyState !== WebSocket.OPEN) {
                this.setStatus();
                throw new Error('Socket连接已断开。');
            }

            let data = '';
            if (angular.isString(message)) {
                data = message
            }
            else if (angular.isObject(message)) {
                data = JSON.stringify(message);
            }
            this.ws.send(data);
        }

        /**
         * 关闭连接
         */
        public close() {
            if (this.ws) {
                this.ws.close();
                this.ws = null;
            }
        }
    }

    angular.module('EStep')
        .service('Socket', Socket);
}
